package craky.componentc;

import java.awt.AlphaComposite;
import java.awt.Composite;
import java.awt.Dimension;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.geom.AffineTransform;

import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicHTML;
import javax.swing.plaf.basic.BasicLabelUI;
import javax.swing.text.View;

import sun.awt.AppContext;
import sun.swing.SwingUtilities2;

public class CLabelUI extends BasicLabelUI
{
    private static CLabelUI extendLabelUI = new CLabelUI();

    private static final Object EXTEND_LABEL_UI_KEY = new Object();

    private Rectangle iconR = new Rectangle();

    private Rectangle textR = new Rectangle();

    private Rectangle contentBounds = new Rectangle();

    public static ComponentUI createUI(JComponent c)
    {
        if(System.getSecurityManager() != null)
        {
            AppContext appContext = AppContext.getAppContext();
            CLabelUI safeExtendLabelUI = (CLabelUI)appContext.get(EXTEND_LABEL_UI_KEY);
            
            if(safeExtendLabelUI == null)
            {
                safeExtendLabelUI = new CLabelUI();
                appContext.put(EXTEND_LABEL_UI_KEY, safeExtendLabelUI);
            }
            
            return safeExtendLabelUI;
        }
        
        return extendLabelUI;
    }
    
    public void paint(Graphics g, JComponent c)
    {
        if(c instanceof JCLabel)
        {
            JCLabel cLabel = (JCLabel)c;
            String text = cLabel.getText();
            Icon icon = (cLabel.isEnabled())? cLabel.getIcon(): cLabel.getDisabledIcon();

            if(icon == null && text == null)
            {
                return;
            }

            Graphics2D g2d = (Graphics2D)g;
            FontMetrics metrics = SwingUtilities2.getFontMetrics(cLabel, g2d);
            Composite oldComposite = g2d.getComposite();
            String clippedText = computeContentBounds(c, g2d, metrics);
            int contentWidth = contentBounds.width;
            int contentHeight = contentBounds.height;
            double theta = Math.toRadians(cLabel.getAngle() % 360);
            double rotateX = contentBounds.x + contentWidth / 2.0;
            double rotateY = contentBounds.y + contentHeight / 2.0;
            Dimension move = computeMove(cLabel, contentWidth, contentHeight, theta);
            AffineTransform trans = g2d.getTransform();
            trans.translate(-move.width, -move.height);
            trans.rotate(theta, rotateX, rotateY);
            g2d.setTransform(trans);

            if(icon != null && cLabel.getIconAlpha() > 0.0f)
            {
                g2d.setComposite(AlphaComposite.SrcOver.derive(cLabel.getIconAlpha()));
                icon.paintIcon(c, g2d, iconR.x, iconR.y);
            }

            if(text != null)
            {
                View view = (View)c.getClientProperty(BasicHTML.propertyKey);
                g2d.translate(cLabel.getDeltaX(), cLabel.getDeltaY());
                
                if(view != null)
                {
                    view.paint(g2d, textR);
                }
                else if(cLabel.getTextAlpha() > 0.0f)
                {
                    int textX = textR.x;
                    int textY = textR.y + metrics.getAscent();
                    g2d.setComposite(AlphaComposite.SrcOver.derive(cLabel.getTextAlpha()));

                    if(cLabel.isEnabled())
                    {
                        paintEnabledText(cLabel, g2d, clippedText, textX, textY);
                    }
                    else
                    {
                        paintDisabledText(cLabel, g2d, clippedText, textX, textY);
                    }
                }

                g2d.translate(-cLabel.getDeltaX(), -cLabel.getDeltaY());
            }
            
            trans.rotate(-theta, rotateX, rotateY);
            trans.translate(move.width, move.height);
            g2d.setTransform(trans);
            g2d.setComposite(oldComposite);
        }
        else
        {
            super.paint(g, c);
        }
    }
    
    private Dimension computeMove(JLabel label, int contentWidth, int contentHeight, double theta)
    {
        int ha = label.getHorizontalAlignment();
        int va = label.getVerticalAlignment();
        boolean leftToRight = label.getComponentOrientation().isLeftToRight();
        double moveX = 0;
        double moveY = 0;
        
        if(ha == JLabel.LEFT || (leftToRight && ha == JLabel.LEADING) || (!leftToRight && ha == JLabel.TRAILING))
        {
            moveX = (contentWidth - (Math.abs(Math.cos(theta) * contentWidth) + Math.abs(Math.sin(theta) * contentHeight))) / 2.0;
        }
        else if(ha == JLabel.RIGHT || (!leftToRight && ha == JLabel.LEADING) || (leftToRight && ha == JLabel.TRAILING))
        {
            moveX = -(contentWidth - (Math.abs(Math.cos(theta) * contentWidth) + Math.abs(Math.sin(theta) * contentHeight))) / 2.0;
        }
        
        if(va == JLabel.TOP)
        {
            moveY = (contentHeight - (Math.abs(Math.sin(theta) * contentWidth) + Math.abs(Math.cos(theta) * contentHeight))) / 2.0;
        }
        else if(va == JLabel.BOTTOM)
        {
            moveY = -(contentHeight - (Math.abs(Math.sin(theta) * contentWidth) + Math.abs(Math.cos(theta) * contentHeight))) / 2.0;
        }
        
        return new Dimension((int)Math.round(moveX), (int)Math.round(moveY));
    }
    
    public void update(Graphics g, JComponent c)
    {
        if(c instanceof JCLabel)
        {
            JCLabel cLabel = (JCLabel)c;
            
            if(cLabel.getBackgroundAlpha() > 0.0f)
            {
                Graphics2D g2d = (Graphics2D)g;
                Composite oldComposite = g2d.getComposite();
                g2d.setComposite(AlphaComposite.SrcOver.derive(cLabel.getBackgroundAlpha()));
                g2d.setColor(c.getBackground());
                g2d.fillRect(0, 0, c.getWidth(), c.getHeight());
                g2d.setComposite(oldComposite);
            }
            
            paint(g, c);
        }
        else
        {
            super.update(g, c);
        }
    }

    protected void paintDisabledText(JLabel label, Graphics g, String text, int textX, int textY)
    {
        if(label instanceof JCLabel)
        {
            JCLabel cLabel = (JCLabel)label;
            int mnemIndex = cLabel.getDisplayedMnemonicIndex();
            g.setColor(cLabel.getDisabledForeground());
            SwingUtilities2.drawStringUnderlineCharAt(cLabel, g, text, mnemIndex, textX, textY);
        }
        else
        {
            super.paintDisabledText(label, g, text, textX, textY);
        }
    }
    
    protected void paintEnabledText(JLabel label, Graphics g, String text, int textX, int textY)
    {
        int mnemIndex = label.getDisplayedMnemonicIndex();
        g.setColor(label.getForeground());
        SwingUtilities2.drawStringUnderlineCharAt(label, g, text, mnemIndex, textX, textY);
    }
    
    private String computeContentBounds(JComponent c, Graphics g, FontMetrics metrics)
    {
        JLabel label = (JLabel)c;
        String text = label.getText();
        Icon icon = (label.isEnabled())? label.getIcon(): label.getDisabledIcon();
        Insets insets = label.getInsets(null);
        String clippedText = null;
        
        if(icon == null && text == null)
        {
            contentBounds.setBounds(0, 0, 0, 0);
        }
        else
        {
            Rectangle viewR = new Rectangle();
            iconR.x = iconR.y = iconR.width = iconR.height = 0;
            textR.x = textR.y = textR.width = textR.height = 0;
            viewR.x = insets.left;
            viewR.y = insets.top;
            viewR.width = c.getWidth() - (insets.left + insets.right);
            viewR.height = c.getHeight() - (insets.top + insets.bottom);
            clippedText = layoutCL(label, metrics, text, icon, viewR, iconR, textR);
            int x1 = Math.min(iconR.x, textR.x);
            int x2 = Math.max(iconR.x + iconR.width, textR.x + textR.width);
            int y1 = Math.min(iconR.y, textR.y);
            int y2 = Math.max(iconR.y + iconR.height, textR.y + textR.height);
            contentBounds.setBounds(x1, y1, x2 - x1, y2 - y1);
        }
        
        return clippedText;
    }
    
    protected void installDefaults(JLabel c)
    {}
}